﻿// 2023/3/8: 首个版本
// 2023/3/20: 修正caller调用
// 2023/4/18: 按键移动至底部。窗口可最大化
// 2023/9/22: 使用PassParent给ShowDialog传参
// 2023/9/25: 改为直接输入Icon对象
// 2024/1/29: 增加显示组件详情标签页。主状态后显示错误提示
// 2024/6/24: 基于API3，支持异步
// 2024/6/28: 恢复方法名
// 2024/10/18: 框架内置组件信息由框架提供
// 2024/11/26: 修正客户端模式下的bug
// 2024/12/3: 使用App.ShowOpen/SaveFileDialog
// 2025/5/28: 增加配置状态筛选框

using System;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Eto.Forms;
using Eto.Drawing;
using ASEva;
using ASEva.Utility;
using ASEva.Samples;
using ASEva.UIEto;

namespace CommonSource
{
    #pragma warning disable CS4014

    class ConfigEditor : DialogPanel
    {
        public ConfigEditor(object caller, Icon icon = null, RelatedGUISettings guiSettings = null)
        {
            this.caller = caller;
            if (guiSettings == null) guiSettings = new RelatedGUISettings();
            this.guiSettings = guiSettings;

            Icon = icon;
            Title = chinese ? "配置编辑器" : "Config Editor";
            SetResizableMode(1000, 600, 1000, 600);

            var mainLayout = this.SetContentAsRowLayout(8, 8, VerticalAlignment.Stretch);
            var leftCol = mainLayout.AddColumnLayout(false, 200, 0, 8);
            var tabPages = mainLayout.AddControl(new TabControl(), true) as TabControl;
            var editLayout = tabPages.AddPage(chinese ? "编辑" : "Edit").SetContentAsColumnLayout();
            var detailsLayout = tabPages.AddPage(chinese ? "详情" : "Details").SetContentAsRowLayout(8, 8, VerticalAlignment.Stretch);
            var detailsLeftLayout = detailsLayout.AddColumnLayout(true);
            var detailsRightLayout = detailsLayout.AddColumnLayout(true);

            entrySearch = leftCol.AddControl(new SearchBox()) as SearchBox;
            var filterLayout = leftCol.AddTableLayout(false, 8, 2);
            list = leftCol.AddControl(new ListBox(), true) as ListBox;
            var statusLayout = editLayout.AddTableLayout(false, 8, 2);

            var filterRow = filterLayout.AddRow();
            checkFilterOK = filterRow.AddCheckBox(chinese ? "正常" : "OK", true, false, true);
            checkFilterDisable = filterRow.AddCheckBox(chinese ? "未启用" : "Disabled", true, false, true);
            filterRow = filterLayout.AddRow();
            checkFilterError = filterRow.AddCheckBox(chinese ? "错误" : "Error", true, false, true);
            checkFilterWarning = filterRow.AddCheckBox(chinese ? "警告" : "Warning", true, false, true);

            var statusRow1 = statusLayout.AddRow();
            statusRow1.AddLabel(chinese ? "主状态:" : "Main status:");
            labelMainStatusValue = statusRow1.AddLabel("-", true);

            var statusRow2 = statusLayout.AddRow();
            statusRow2.AddLabel(chinese ? "子状态:" : "Child status:");
            labelChildStatusValue = statusRow2.AddLabel("-", true);

            textConfig = editLayout.AddControl(new TextArea(), true) as TextArea;
            textConfig.Enabled = false;

            var buttonsLayout = editLayout.AddRowLayout();
            var buttonImport = buttonsLayout.AddButton(chinese ? "导入" : "Import", false, 80);
            var buttonExport = buttonsLayout.AddButton(chinese ? "导出" : "Export", false, 80);
            buttonDisable = buttonsLayout.AddButton(chinese ? "禁用" : "Disable", false, 80);
            buttonsLayout.AddSpace();
            buttonGUI = buttonsLayout.AddButton(chinese ? "打开图形界面编辑" : "Edit with GUI", false, 120);
            buttonXml = buttonsLayout.AddButton(chinese ? "打开XML编辑器" : "Edit as XML", false, 120);
            var buttonEdit = buttonsLayout.AddButton(chinese ? "编辑" : "Edit", false, 80);
            var buttonSave = buttonsLayout.AddButton(chinese ? "保存" : "Save", false, 80);
            buttonImport.Enabled = buttonExport.Enabled = buttonDisable.Enabled = buttonGUI.Enabled = buttonXml.Enabled = buttonEdit.Enabled = buttonSave.Enabled = false;

            var subLayout = detailsLeftLayout.AddColumnLayout(true, 2);
            subLayout.AddLabel(chinese ? "输出样本" : "Output Samples");
            textOutputSamples = subLayout.AddControl(new TextArea{ ReadOnly = true }, true) as TextArea;

            subLayout = detailsLeftLayout.AddColumnLayout(true, 2);
            subLayout.AddLabel(chinese ? "输出信号" : "Output Signals");
            textOutputSignals = subLayout.AddControl(new TextArea{ ReadOnly = true }, true) as TextArea;

            subLayout = detailsRightLayout.AddColumnLayout(true, 2);
            subLayout.AddLabel(chinese ? "输出场景" : "Output Scenarios");
            textOutputScenes = subLayout.AddControl(new TextArea{ ReadOnly = true }, true) as TextArea;

            subLayout = detailsLeftLayout.AddColumnLayout(true, 2);
            subLayout.AddLabel(chinese ? "输出报告" : "Output Reports");
            textOutputGraphs = subLayout.AddControl(new TextArea{ ReadOnly = true }, true) as TextArea;

            subLayout = detailsRightLayout.AddColumnLayout(true, 2);
            subLayout.AddLabel(chinese ? "需要信号打包" : "Required Signal Packings");
            textRequiredSignalPackings = subLayout.AddControl(new TextArea{ ReadOnly = true }, true) as TextArea;

            subLayout = detailsRightLayout.AddColumnLayout(true, 2);
            subLayout.AddLabel(chinese ? "需要视频通道" : "Required Video Channels");
            textRequiredVideoChannels = subLayout.AddControl(new TextArea{ ReadOnly = true }, true) as TextArea;

            subLayout = detailsRightLayout.AddColumnLayout(true, 2);
            subLayout.AddLabel(chinese ? "需要数据类型（采集用）" : "Required Data Types (For Recording)");
            textRequiredDataTypes = subLayout.AddControl(new TextArea{ ReadOnly = true }, true) as TextArea;

            updateList(null);

            entrySearch.TextChanged += delegate
            {
                updateList(entrySearch.Text);
            };

            checkFilterDisable.CheckedChanged += delegate
            {
                updateList(entrySearch.Text);
            };

            checkFilterOK.CheckedChanged += delegate
            {
                updateList(entrySearch.Text);
            };

            checkFilterError.CheckedChanged += delegate
            {
                updateList(entrySearch.Text);
            };

            checkFilterWarning.CheckedChanged += delegate
            {
                updateList(entrySearch.Text);
            };  

            list.SelectedIndexChanged += delegate
            {
                if (list.SelectedIndex < 0)
                {
                    labelMainStatusValue.Text = "-";
                    labelMainStatusValue.TextColor = Colors.Black;
                    labelChildStatusValue.Text = "-";
                    buttonEdit.Enabled = false;
                    buttonSave.Enabled = false;
                    buttonDisable.Enabled = false;
                    buttonXml.Enabled = false;
                    buttonImport.Enabled = false;
                    buttonExport.Enabled = false;
                    textConfig.Enabled = false;
                    textConfig.Text = "";
                    guiOptions = null;
                    guiEntries = null;
                    buttonGUI.Enabled = false;
                    textOutputSamples.Text = textOutputSignals.Text = textOutputScenes.Text = textOutputGraphs.Text = "";
                    textRequiredSignalPackings.Text = textRequiredVideoChannels.Text = textRequiredDataTypes.Text = "";
                }
                else
                {
                    var id = list.SelectedKey;
                    updateMainStatusText(id);
                    updateChildStatusText(id);
                    buttonEdit.Enabled = true;
                    buttonSave.Enabled = false;
                    updateButtonDisable(id);
                    buttonImport.Enabled = true;
                    buttonExport.Enabled = true;
                    textConfig.Enabled = false;
                    updateTextAndXmlButton(id);
                    updateDetails(id);
                }
            };

            buttonEdit.Click += delegate
            {
                buttonEdit.Enabled = false;
                buttonSave.Enabled = true;
                buttonDisable.Enabled = false;
                buttonXml.Enabled = false;
                buttonImport.Enabled = false;
                buttonExport.Enabled = false;
                textConfig.Enabled = true;
                list.Enabled = false;
                entrySearch.Enabled = false;  
            };

            buttonSave.Click += async delegate
            {
                buttonEdit.Enabled = true;
                buttonSave.Enabled = false;
                buttonImport.Enabled = true;
                buttonExport.Enabled = true;
                textConfig.Enabled = false;
                list.Enabled = true;
                entrySearch.Enabled = true;

                var id = list.SelectedKey;
                await AgencyAsync.SetModuleConfig(caller, id, textConfig.Text);
                if (list.SelectedKey == id) updateAfterEdit(id);
            };

            buttonDisable.Click += async delegate
            {
                var id = list.SelectedKey;
                await AgencyAsync.DisableModule(caller, id);
                if (list.SelectedKey == id) updateAfterEdit(id);
            };

            buttonXml.Click += async delegate
            {
                var dialog = new XmlEditor(textConfig.Text, icon);
                await App.RunDialog(dialog);
                if (dialog.BoolResult)
                {
                    var id = list.SelectedKey;
                    await AgencyAsync.SetModuleConfig(caller, id, dialog.XmlString);
                    if (list.SelectedKey == id) updateAfterEdit(id);
                }
            };

            buttonGUI.Click += async delegate
            {
                ModuleRelatedUIEntry entry = null;
                if (guiOptions.Length > 1)
                {
                    var selector = new ComboSelector(guiOptions, icon, chinese);
                    await App.RunDialog(selector);
                    if (selector.BoolResult) entry = guiEntries[selector.IntResult];
                    else return;
                }
                else entry = guiEntries[0];
                
                bool handled = false;
                if (entry.Type == ModuleRelatedUIType.Dialog)
                {
                    if (OpenDialog != null) handled = await OpenDialog.Invoke(entry.ClassID, entry.TransformID);
                }
                else if (entry.Type == ModuleRelatedUIType.Window)
                {
                    if (AddWindow != null) handled = await AddWindow.Invoke(entry.ClassID, entry.TransformID);
                }
                else if (entry.Type == ModuleRelatedUIType.Console)
                {
                    if (RunConsole != null) handled = await RunConsole.Invoke(entry.ClassID);
                }
                if (!handled) AgencyLocal.PopupError(chinese ? "打开GUI失败。" : "Failed to open GUI.");
                else updateAfterEdit(list.SelectedKey);
            };

            buttonImport.Click += async delegate
            {
                var selected = await App.ShowOpenFileDialog(this, null, false, null, new()
                {
                    [chinese ? "模块配置文件 (*.asmc)" : "Module Config (*.asmc)"] = [".asmc"],
                });
                if (selected != null)
                {
                    var id = list.SelectedKey;
                    await importConfig(selected[0], id);
                    if (list.SelectedKey == id) updateAfterEdit(id);
                }
            };

            buttonExport.Click += async delegate
            {
                var id = list.SelectedKey;
                var selected = await App.ShowSaveFileDialog(this, null, Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), list.SelectedValue.ToString(), chinese ? "模块配置文件 (*.asmc)" : "Module Config (*.asmc)", ".asmc");
                if (selected != null)
                {
                    buttonExport.Enabled = false;
                    exportConfig(selected, id);
                }
            };
        }

        public delegate Task<bool> AddWindowHandler(String classID, String transformID);
        public delegate Task<bool> OpenDialogHandler(String classID, String transformID);
        public delegate Task<bool> RunConsoleHandler(String classID);

        public event AddWindowHandler AddWindow;
        public event OpenDialogHandler OpenDialog;
        public event RunConsoleHandler RunConsole;

        private void updateAfterEdit(String id)
        {
            updateMainStatusText(id);
            updateChildStatusText(id);
            updateTextAndXmlButton(id);
            updateDetails(id);
            updateButtonDisable(id);
        }

        private async Task updateButtonDisable(String id)
        {
            buttonDisable.Enabled = false;
            var notDisabled = (await AgencyAsync.GetModuleConfigStatus(caller, id)).Item1 != ConfigStatus.Disabled;
            if (list.SelectedKey == id) buttonDisable.Enabled = notDisabled;
        }

        private async Task updateTextAndXmlButton(String id)
        {
            buttonXml.Enabled = false;
            textConfig.Text = "";

            var configString = await AgencyAsync.GetModuleConfig(caller, id);
            if (configString == null) configString = "";
            if (list.SelectedKey != id) return;

            String niceXmlString;
            bool isXml = checkIsXml(configString, out niceXmlString);

            buttonXml.Enabled = isXml;
            textConfig.Text = isXml ? niceXmlString : configString;
        }

        private async Task updateDetails(String id)
        {
            buttonGUI.Enabled = false;
            textOutputSamples.Text = textOutputSignals.Text = textOutputScenes.Text = textOutputGraphs.Text = "";
            textRequiredSignalPackings.Text = textRequiredVideoChannels.Text = textRequiredDataTypes.Text = "";

            var details = await AgencyAsync.GetModuleDetails(id);
            if (details == null) return;
            if (list.SelectedKey != id) return;

            var options = new List<String>();
            var entries = new List<ModuleRelatedUIEntry>();
            if (details.RelatedUIEntries != null)
            {
                foreach (var e in details.RelatedUIEntries)
                {
                    String typeText = null;
                    switch (e.Type)
                    {
                        case ModuleRelatedUIType.Dialog:
                            if (guiSettings.EnableDialogs) typeText = chinese ? "对话框) " : "Dialog) ";
                            break;
                        case ModuleRelatedUIType.Window:
                            if (guiSettings.EnableWindows) typeText = chinese ? "窗口) " : "Window) ";
                            break;
                        case ModuleRelatedUIType.Console:
                            if (guiSettings.EnableConsoles) typeText = chinese ? "控制台) " : "Console) ";
                            break;
                    }
                    if (typeText != null)
                    {
                        options.Add(typeText + e.Name);
                        entries.Add(e);
                    }
                }
            }
            guiOptions = options.ToArray();
            guiEntries = entries.ToArray();
            buttonGUI.Enabled = guiOptions.Length > 0;
            updateDetailsText(details);
        }

        private void updateDetailsText(ModuleDetails details)
        {
            if (details == null)
            {
                textOutputSamples.Text = textOutputSignals.Text = textOutputScenes.Text = textOutputGraphs.Text = "";
                textRequiredSignalPackings.Text = textRequiredVideoChannels.Text = textRequiredDataTypes.Text = "";
                return;
            }

            var sampleRows = new List<String>();
            if (details.OutputSamples != null) foreach (var pair in details.OutputSamples) sampleRows.Add(pair.Value + " (" + pair.Key + ")");
            textOutputSamples.Text = String.Join('\n', sampleRows);

            var signalRows = new List<String>();
            if (details.OutputSignals != null)
            {
                foreach (var pair in details.OutputSignals)
                {
                    if (pair.Value == null || pair.Value.Length == 0) continue;
                    var prefix = String.IsNullOrWhiteSpace(pair.Key) ? "" : (pair.Key + " -> ");
                    foreach (var name in pair.Value) signalRows.Add(prefix + name);
                }
            }
            textOutputSignals.Text = String.Join('\n', signalRows);

            textOutputScenes.Text = details.OutputScenes == null ? "" : String.Join('\n', details.OutputScenes);
            textOutputGraphs.Text = details.OutputGraphs == null ? "" : String.Join('\n', details.OutputGraphs);
            textRequiredSignalPackings.Text = details.RequiredSignalPackings == null ? "" : String.Join('\n', details.RequiredSignalPackings);
            textRequiredVideoChannels.Text = details.RequiredVideoChannels == null ? "" : String.Join('\n', details.RequiredVideoChannels);
            textRequiredDataTypes.Text = details.RequiredRecordDataTypes == null ? "" : String.Join('\n', details.RequiredRecordDataTypes);
        }

        private async Task updateList(String key)
        {
            var items = (await genItems(key)).ToArray();
            var tasks = new Task<(ConfigStatus, String)>[items.Length].Populate((i) =>
            {
                return AgencyAsync.GetModuleConfigStatus(caller, items[i].ID);
            });
            var results = await Task.WhenAll(tasks);
            if (key != null && entrySearch.Text != key || results.Length != items.Length) return;

            list.Items.Clear();

            for (int i = 0; i < items.Length; i++)
            {
                var item = items[i];
                var result = results[i];
                switch (result.Item1)
                {
                    case ConfigStatus.Disabled:
                        if (!checkFilterDisable.Checked.Value) continue;
                        break;
                    case ConfigStatus.Enabled:
                        if (!checkFilterOK.Checked.Value) continue;
                        break;
                    case ConfigStatus.EnabledWithError:
                        if (!checkFilterError.Checked.Value) continue;
                        break;
                    case ConfigStatus.EnabledWithWarning:
                        if (!checkFilterWarning.Checked.Value) continue;
                        break;
                }
                list.Items.Add(item.Name, item.ID);
            }
        }

        private async Task<List<ConfigEditorItem>> genItems(String key)
        {
            var fullList = new List<ConfigEditorItem>();
            foreach (var pair in await AgencyAsync.GetProcessorClassTable())
            {
                fullList.Add(new ConfigEditorItem() { ID = pair.Key, Name = pair.Value.Title });
            }
            foreach (var pair in await AgencyAsync.GetNativeClassTable())
            {
                fullList.Add(new ConfigEditorItem() { ID = pair.Key, Name = pair.Value.Title });
            }
            foreach (var pair in await AgencyAsync.GetDeviceClassTable())
            {
                fullList.Add(new ConfigEditorItem() { ID = pair.Key, Name = pair.Value.Title });
            }
            
            key = key == null ? "" : key.ToLower();
            var filterList = new List<ConfigEditorItem>();
            foreach (var item in fullList)
            {
                if (key.Length == 0) filterList.Add(item);
                else
                {
                    if (item.Name.ToLower().Contains(key)) filterList.Add(item);
                }
            }

            filterList.Sort();
            return filterList;
        }

        private async Task updateMainStatusText(String id)
        {
            labelMainStatusValue.Text = "";

            var status = await AgencyAsync.GetModuleConfigStatus(caller, id);
            if (list.SelectedKey != id) return;

            var color = Colors.Black;
            String text = "";
            switch (status.Item1)
            {
                case ConfigStatus.Disabled:
                    color = Colors.DimGray;
                    text = chinese ? "未启用" : "Disabled";
                    break;
                case ConfigStatus.Enabled:
                    color = Colors.Green;
                    text = chinese ? "正常" : "OK";
                    break;
                case ConfigStatus.EnabledWithError:
                    color = Colors.Red;
                    text = chinese ? "错误" : "Error";
                    break;
                case ConfigStatus.EnabledWithWarning:
                    color = Colors.DarkOrange;
                    text = chinese ? "警告" : "Warning";
                    break;
                default:
                    text = chinese ? "未知" : "Unknown";
                    break;
            }

            if (!String.IsNullOrWhiteSpace(status.Item2)) text = text + " (" + status.Item2 + ")";

            labelMainStatusValue.Text = text;
            labelMainStatusValue.TextColor = color;
        }

        private async Task updateChildStatusText(String id)
        {
            labelChildStatusValue.Text = "";
            var childStatusText = await getChildStatusText(id);;
            if (list.SelectedKey == id) labelChildStatusValue.Text = childStatusText;
        }

        private async Task<string> getChildStatusText(String id)
        {
            var status = await AgencyAsync.GetModuleChildConfigStatus(caller, id);
            if (status == null || status.Length == 0) return "-";

            var textList = new List<String>();
            foreach (var s in status)
            {
                switch (s)
                {
                    case ConfigStatus.Disabled:
                        textList.Add(chinese ? "未启用" : "Disabled");
                        break;
                    case ConfigStatus.Enabled:
                        textList.Add(chinese ? "正常" : "OK");
                        break;
                    case ConfigStatus.EnabledWithError:
                        textList.Add(chinese ? "错误" : "Error");
                        break;
                    case ConfigStatus.EnabledWithWarning:
                        textList.Add(chinese ? "警告" : "Warning");
                        break;
                    default:
                        textList.Add(chinese ? "未知" : "Unknown");
                        break;
                }
            }

            return String.Join(", ", textList);
        }

        private async Task importConfig(String filePath, String id)
        {
            var xml = new XmlDocument();
            try { xml.Load(filePath); }
            catch (Exception)
            {
                await AgencyLocal.PopupError(chinese ? "无法打开配置文件。" : "Failed to load configuration file.");
                return;
            }

            var rootNode = xml.DocumentElement;
            var typeName = rootNode.Attributes["type"].Value.ToLower();

            if (typeName == id)
            {
                await AgencyAsync.SetModuleConfig(caller, id, Encoding.UTF8.GetString(Convert.FromBase64String(rootNode.InnerText)));
            }
            else
            {
                await AgencyLocal.PopupError(chinese ? "配置文件类型不匹配。" : "The type of configuration file dismatched.");
            }
        }

        private async Task exportConfig(String filePath, String id)
        {
            var config = await AgencyAsync.GetModuleConfig(caller, id);
            if (config == null) config = "";

            var xml = Xml.Create("root");
            var root = xml.DocumentElement;
            root.Attributes.Append(xml.CreateAttribute("type")).Value = id;
            root.InnerText = Convert.ToBase64String(Encoding.UTF8.GetBytes(config));
            xml.Save(filePath);

            AgencyLocal.Log(filePath + (chinese ? "已保存" : " saved."), LogLevel.Info);
        }

        private bool checkIsXml(String str, out String niceXmlString)
        {
            niceXmlString = "";
            if (str == null) str = "";
            if (str.StartsWith("<?xml"))
            {
                var xml = new XmlDocument();
                try
                {
                    xml.LoadXml(str);
                    niceXmlString = toNiceString(xml);
                    return true;
                }
                catch (Exception) {}
            }
            return false;
        }

        private String toNiceString(XmlDocument xml)
        {
            if (xml == null) return "";

            var memoryStream = new MemoryStream();

            var settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.IndentChars = "    ";

            var xmlWriter = XmlWriter.Create(memoryStream, settings);
            xml.Save(xmlWriter);
            xmlWriter.Close();

            memoryStream.Position = 3; // BOM
            var data = new byte[memoryStream.Length - 3];
            memoryStream.Read(data, 0, (int)memoryStream.Length - 3);
            memoryStream.Close();

            return Encoding.UTF8.GetString(data);
        }

        private class ComboSelector : DialogPanel
        {
            public ComboSelector(String[] options, Icon icon, bool chinese)
            {
                Icon = icon;
                Title = chinese ? "选择GUI" : "Select GUI";
                this.SetFixMode(600, 60, true);

                var row = this.SetContentAsRowLayout();
                var combo = row.AddComboBox(options, true);
                row.AddButton(chinese ? "打开" : "Open", false, 60).Click += delegate
                {
                    BoolResult = true;
                    IntResult = combo.SelectedIndex;
                    Close();
                };
            }
        }

        private ListBox list;
        private Label labelMainStatusValue, labelChildStatusValue;
        private Button buttonDisable, buttonXml;
        private TextArea textConfig;
        private TextArea textOutputSamples, textOutputSignals, textOutputScenes, textOutputGraphs;
        private TextArea textRequiredSignalPackings, textRequiredVideoChannels, textRequiredDataTypes;
        private Button buttonGUI;
        private SearchBox entrySearch;
        private CheckBox checkFilterDisable, checkFilterOK, checkFilterWarning, checkFilterError;

        private bool chinese = AgencyLocal.GetAppLanguage() == Language.Chinese;

        private object caller;
        private String[] guiOptions = null;
        private ModuleRelatedUIEntry[] guiEntries = null;
        private RelatedGUISettings guiSettings = null;
    }

    class ConfigEditorItem : IComparable
    {
        public String ID { get; set; }
        public String Name { get; set; }

        public int CompareTo(object obj)
        {
            return Name.CompareTo((obj as ConfigEditorItem).Name);
        }

        public override string ToString()
        {
            return Name;
        }
    }

    class RelatedGUISettings
    {
        public bool EnableDialogs { get; set; }
        public bool EnableWindows { get; set; }
        public bool EnableConsoles { get; set; }

        public RelatedGUISettings(bool enableDialogs = true, bool enableWindows = false, bool enableConsoles = false)
        {
            EnableDialogs = enableDialogs;
            EnableWindows = enableWindows;
            EnableConsoles = enableConsoles;
        }
    }
}